:- module(parse,[term/3,parse_string/2,parse_file/2]).
:- use_module(operator).
:- use_module(token).

term(Term) --> {op_table(OpTable)}, term(OpTable, Term).
term(without_comma_operator,Term) --> {op_table(RawTable),filter(_/xfy/',',RawTable,OpTable)}, term(OpTable, Term).
term(OpTable, Term) --> glom(Vine), {tangle(OpTable,Vine,Term)}.

filter(_,[],[]).
filter(E,[E|Es],Fs) :- filter(E,Es,Fs).
filter(E,[F|Es],[F|Fs]) :- E \= F, filter(E,Es,Fs).

glom([term(Term)|Gs]) --> single(Term), glom(Gs).
glom([term(Term)|Gs]) --> ['('], term(Term), [')'], glom(Gs).
glom([term(compound(',',[]))|Gs]) --> [','], glom(Gs).
glom([]) --> [].

single(variable(Name))        --> [variable(Codes)],
 {atom_codes(Name, Codes)}.
single(compound('{}',[Body])) --> ['{'], term(Body), ['}'].
single(compound(Name,[]))   --> [atom(Codes)], {atom_codes(Name, Codes)}.
single(compound(Name,Args))   --> [open_atom(Codes)], compound_body(Args), [')'],
 {atom_codes(Name, Codes)}.
single(compound('[]',[]))     --> ['['],[']'].
single(List)                  --> ['['], list_body(List), [']'].
single(String)                --> [string(Codes)], {stringify_string(Codes,String)}.

stringify_string([],compound('[]',[])).
stringify_string([C|Cs],compound('.',[compound(C,[]),Xs])) :- stringify_string(Cs,Xs).


compound_body([T|Ts])           --> term(without_comma_operator,T),
 ( [','] -> compound_body(Ts) ; {Ts = []} ).
list_body(compound('.',[T,Ts])) --> term(without_comma_operator,T),
 ( [','] -> list_body(Ts)
 ; ['|'] -> term(Ts)
 ; {Ts = compound('[]',[])} ).


%% tangle may report errors such as infix clashing?
tangle([],[term(Term)],Term).
tangle([_/xfx/O|Os],Terms,compound(O,[LHSt,RHSt])) :- split(Terms, term(compound(O,[])), LHS-RHS),
 tangle(Os,LHS,LHSt), tangle(Os,RHS,RHSt).
tangle([_/xfy/O|Os],Terms,compound(O,[LHSt,RHSt])) :- split(Terms, term(compound(O,[])), LHS-RHS),
 tangle(Os,LHS,LHSt), tangle([_/xfy/O|Os],RHS,RHSt).
tangle([_/yfx/O|Os],Terms,compound(O,[LHSt,RHSt])) :- split(Terms, term(compound(O,[])), LHS-RHS),
 tangle([_/yfx/O|Os],LHS,LHSt), tangle(Os,RHS,RHSt).
tangle([_/fx/O|Os],[term(compound(O,[]))|Tail],compound(O,[T])) :- tangle(Os,Tail,T).
tangle([_/fy/O|Os],[term(compound(O,[]))|Tail],compound(O,[T])) :- tangle([_/fy/O|Os],Tail,T).
tangle([_|Os],Vine,Term) :- tangle(Os,Vine,Term).

parse_string(String, Term) :-
 tokenize(Tokens, String, "."),
 term(Term, Tokens, []).

parse_file(Filename, Parse) :-
 get_file_contents(Filename, Contents),
 parse_file(Parse, Contents, []).
parse_file([], Whitespace, []) :-
 whitespace(Whitespace,[]).
parse_file([Parse|Parses], String, End) :-
 tokenize(Tokens, String, [0'.|Remain]),
 term(Parse, Tokens, []),!, %% Don't parse same thing again
 ( ir_operator(Parse, Level, Fixity, Name) ->
   '$foo$op'(Level, Fixity, Name)
 ; true ),               !, %% Don't add the same operator again
 write('%%% '),write(debug-info),write(' '),write(Parse),nl,nl,
 parse_file(Parses, Remain, End).


ir_operator(compound((:-),[compound(op,[compound(Level,[])
                                       ,compound(Fixity,[])
                                       ,compound(Name,[])])]),
            LevelN,Fixity,Name) :-
 atom_number(Level,LevelN).
